package middleware

import (
	"bytes"
	"encoding/json"
	"fmt"
	"haiqiu-go-admin/config"
	"haiqiu-go-admin/dao"
	"haiqiu-go-admin/entity"
	"haiqiu-go-admin/entity/dto"
	"haiqiu-go-admin/result"
	"haiqiu-go-admin/utils"
	"io"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"
)

//type LogMiddleware struct {
//	// 可以在这里添加操作数据的依赖
//}

//func NewLogMiddleware() *LogMiddleware {
//	return &LogMiddleware{}
//}

type logResponseWriter struct {
	writer http.ResponseWriter
	code   int
	// 存响应数据
	buf *bytes.Buffer
}

func newLogResponseWriter(writer http.ResponseWriter, code int) *logResponseWriter {
	var buf bytes.Buffer
	return &logResponseWriter{
		writer: writer,
		code:   code,
		buf:    &buf,
	}
}

func (w *logResponseWriter) Write(bs []byte) (int, error) {
	w.buf.Write(bs)
	return w.writer.Write(bs)
}

func (w *logResponseWriter) Header() http.Header {
	return w.writer.Header()
}

func (w *logResponseWriter) WriteHeader(code int) {
	w.code = code
	w.writer.WriteHeader(code)
}

func Logger() Middleware {
	return func(handlerFunc http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			//判断是否为OPTIONS请求，跨域请求，非跨域请求不会出现options请求，
			//自定义请求头，请求头中的content-type是application/x-www-form-urlencoded，multipart/form-data，text/plain之外的格式
			if r.Method == http.MethodOptions {
				Cors(w, r)
				return
			}
			startMilli := time.Now().UnixMilli()
			var dup io.ReadCloser
			dup, r.Body, _ = drainBody(r.Body)
			lwr := newLogResponseWriter(w, http.StatusOK)
			//执行下一步中间件或业务处理
			handlerFunc(lwr, r)
			r.Body = dup
			// 将自己逻辑写在logDetailLogic
			var wg sync.WaitGroup
			wg.Add(2)
			go func(w http.ResponseWriter, r *http.Request) {
				defer wg.Done()
				logDetailLogic(r, lwr)
			}(w, r)
			go func(w http.ResponseWriter, r *http.Request) {
				defer wg.Done()
				loginLogDetailLogic(r, lwr)
			}(w, r)
			wg.Wait()
			//响应日志
			if r.Method != http.MethodOptions {
				sprintf := fmt.Sprintf("获取APi请求：%v=======方法：%v=========耗时：%v毫秒", r.URL.Path, r.Method,
					time.Now().UnixMilli()-startMilli)
				log.Println(sprintf)
			}
		}
	}
}

func logDetailLogic(request *http.Request, response *logResponseWriter) {
	var requestParam string
	var responseJson string
	var res = result.Res{}
	loggerUrls := config.AppConfig.Logger.Urls
	log.Println(loggerUrls)
	var flag bool
	for _, v := range loggerUrls {
		if v == request.URL.Path {
			log.Println("检测到为上传文件接口，忽略参数")
			flag = true
		}
	}
	if request.Body != nil && !flag {
		bs, _ := io.ReadAll(request.Body)
		log.Println("请求内容：", string(bs))
		requestParam = string(bs)

	}
	if len(response.buf.Bytes()) > 0 {
		contentType := response.Header().Get("Content-Type")
		if contentType != "image/png" {
			log.Println("响应内容：", response.buf.String())
			responseJson = response.buf.String()
			var b = []byte(response.buf.String())
			err := json.Unmarshal(b, &res)
			if err != nil {
				panic(err)
			}
		}
	}
	_, _, username, _ := utils.Token{}.GetUserTokenAndUserIdAndUsername(nil, request)

	var nowTime = time.Now()
	res.Data = "*******"
	userAgent := request.Header.Get("User-Agent")
	var sysLog = entity.SysLog{
		Operation:      &request.URL.Path,
		RequestParam:   requestParam,
		ResponseJson:   &responseJson,
		RequestIp:      request.RemoteAddr,
		RequestAddress: request.RemoteAddr,
		Username:       username,
		RestUrl:        &request.URL.Path,
		Result:         strconv.Itoa(res.Code),
		Os:             &userAgent,
		Method:         request.Method,
		Browser:        &userAgent,
		CreateTime:     &nowTime,
		UpdateTime:     &nowTime,
	}
	row, err := dao.LogDao{}.LogAdd(&sysLog)
	if err != nil {
		panic(err)
	}
	sprintf := fmt.Sprintf("系统操作写入日志%v行", row)
	log.Println(sprintf)
}

func loginLogDetailLogic(request *http.Request, response *logResponseWriter) {
	if request.URL.Path != "/login" {
		return
	}
	var responseJson string
	var loginDto dto.LoginDto

	var res = result.Res{}
	if request.Body != nil {
		bs, _ := io.ReadAll(request.Body)
		log.Println("登录请求内容：", string(bs))
		err := json.Unmarshal(bs, &loginDto)
		if err != nil {
			log.Println(err.Error())
		}
	}
	if len(response.buf.Bytes()) > 0 {
		log.Println("登录响应内容：", response.buf.String())
		responseJson = response.buf.String()
		var b = []byte(response.buf.String())
		err := json.Unmarshal(b, &res)
		if err != nil {
			panic(err)
		}
	}
	if res.Code == 200 {
		res.Message = "登录成功"
	}
	loginDto.Password = "*****"
	marshal, err := json.Marshal(loginDto)
	if err != nil {
		panic(err)
	}
	var nowTime = time.Now()
	userAgent := request.Header.Get("User-Agent")

	addr := utils.Web{}.GetAddr(request.RemoteAddr)
	//登录日志
	var sysLoginLog = entity.SysLoginLog{
		RequestParam:   string(marshal),
		ResponseJson:   &responseJson,
		RequestIp:      request.RemoteAddr,
		RequestAddress: &addr,
		Username:       loginDto.Username,
		RestUrl:        &request.URL.Path,
		Result:         res.Message,
		Os:             &userAgent,
		Browser:        &userAgent,
		CreateTime:     &nowTime,
		UpdateTime:     &nowTime,
	}
	row, err := dao.LoginLogDao{}.LoginLogAdd(&sysLoginLog)
	if err != nil {
		log.Println(err.Error())
	}
	sprintf := fmt.Sprintf("系统登录写入日志%v行", row)
	log.Println(sprintf)
}

// drainBody from httputil.drainBody
func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
	if b == nil || b == http.NoBody {
		// No copying needed. Preserve the magic sentinel meaning of NoBody.
		return http.NoBody, http.NoBody, nil
	}
	var buf bytes.Buffer
	if _, err = buf.ReadFrom(b); err != nil {
		return nil, b, err
	}
	if err = b.Close(); err != nil {
		return nil, b, err
	}
	return io.NopCloser(&buf), io.NopCloser(bytes.NewReader(buf.Bytes())), nil
}
